1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.array;
12 private import hip.util.conv : to;
13 import std.traits : hasElaborateCopyConstructor;
14 version(WebAssembly) version = UseCustomRT;
15 version(PSVita) version = UseCustomRT;
16 
17 /**
18 * Uses accessor on the array to find an element
19 */
20 int indexOf(string accessor, T, Q)(T[] arr, Q element, int startIndex = 0)
21 {
22     static if(accessor != "")
23         enum op = "."~accessor;
24     else
25         enum op = accessor;
26     const size_t len = arr.length;
27     for(size_t i = startIndex; i < len; i++)
28         mixin("if(arr[i]"~op~" == element)return cast(int)i;");
29     return -1;
30 }
31 /**
32 * Returns index of element if it finds or returns -1 if not
33 */
34 int indexOf(T)(in T[] arr, T element, int startIndex = 0) pure nothrow @nogc
35 {
36     if(startIndex < 0)
37         return -1;
38     for(int i = startIndex; i < arr.length; i++)
39         if(arr[i] == element)
40             return i;
41     return -1;
42 }
43 import hip.util.reflection;
44 
45 ///Index of for tuples
46 int indexOf(T, V)(in T tuple, V value) pure nothrow @nogc if(!isArray!T)
47 {
48     foreach(i, v; tuple)
49     {
50         static if(is(typeof(v) == V))
51         if(v == value)
52             return i;
53     }
54     return -1;
55 }
56 
57 
58 
59 int lastIndexOf(T)(T[] arr, T element, int startIndex = -1)
60 {
61     const size_t len = arr.length;
62     if(len==0)return-1;
63     if(startIndex < 0) startIndex = (cast(int)len - 1);
64     for(int i = startIndex; i >= 0; i--)
65         if(arr[i] == element)
66             return i;
67     return -1;
68 }
69 
70 /**
71 *   Should work only for numerics
72 */
73 int binarySearch(T)(in T[] arr, T element) @nogc @safe nothrow
74 {
75     uint l = 0;
76     uint r = arr.length;
77     uint m;
78     while(l <= r)
79     {
80         m = cast(uint)((l+r)/2);
81         if(arr[m] < element)
82             l = m + 1;
83         else if(arr[m] > element)
84             r = m - 1;
85         else if(arr[m] == element)
86             return m;
87     }
88 
89     return -1;
90 }
91 
92 bool swapAt(T)(T[] arr, int index1, int index2) @nogc @safe nothrow
93 {
94     if(index1 == index2 || index1 < 0 || index1 >= arr.length || index2 < 0 || index2 >= arr.length)
95         return false;
96     T temp  = arr[index1];
97     arr[index1] = arr[index2];
98     arr[index2] = temp;
99     return true;
100 }
101 
102 /**
103 *  Returns if swap was succesful
104 */
105 bool swapElementsFromArray(T)(T[] arr, T element1, T element2) @nogc @safe nothrow
106 {
107     long index1 = arr.indexOf(element1);
108     long index2 = arr.indexOf(element2);
109 
110     if(index1 != -1 && index2 != 1)
111     {
112         T temp = arr[index1];
113         arr[index1] = arr[index2];
114         arr[index2] = temp;
115         return true;
116     }
117     return false;
118 }
119 
120 
121 bool popFront(T)(ref T[] arr, out T val)
122 {
123     import hip.util.memory;
124     if(arr.length == 0)
125         return false;
126     val = arr[0];
127     memcpy(arr.ptr, arr.ptr+1, (cast(int)arr.length-1)*T.sizeof);
128     arr.length--;
129     return true;
130 }
131 bool popFront(T)(ref T[] arr)
132 {
133     T val;
134     return popFront(arr, val);
135 }
136 bool remove(T)(ref T[] arr, T val)
137 {
138     for(size_t i = 0; i < arr.length; i++)
139     {
140         if(arr[i] == val)
141         {
142             static if(hasElaborateCopyConstructor!T)
143             {
144                 for(size_t z = 0; z+i < cast(int)arr.length-1; z++)
145                     arr[z+i] = arr[z+i+1];
146             }
147             else
148             {
149                 import core.stdc.string;
150                 size_t moveElements = arr.length - i - 1;
151                 if(moveElements)
152                     memmove(&arr[i], &arr[i+1], moveElements * T.sizeof);
153             }
154             arr.length--;
155             return true;
156         }
157     }
158     return false;
159 }
160 
161 bool remove(T)(ref T[] arr, const(T)* val)
162 {
163     for(size_t i = 0; i < arr.length; i++)
164     {
165         if(&arr[i] == val)
166         {
167             static if(hasElaborateCopyConstructor!T)
168             {
169                 for(size_t z = 0; z+i < cast(int)arr.length-1; z++)
170                     arr[z+i] = arr[z+i+1];
171             }
172             else
173             {
174                 import core.stdc.string;
175                 size_t moveElements = arr.length - i - 1;
176                 if(moveElements)
177                     memmove(&arr[i], &arr[i+1], moveElements * T.sizeof);
178             }
179             arr.length--;
180             return true;
181         }
182     }
183     return false;
184 }
185 
186 pragma(inline, true)
187 bool contains(T)(in T[] arr, T val) pure nothrow @nogc {return arr.indexOf(val) != -1;} 
188 
189 pragma(inline, true)
190 bool contains(T, V)(in T[] tuple, V val) pure nothrow @nogc {return tuple.indexOf(val) != -1;} 
191 
192 
193 /**
194 *   Compare a array of structures member with a target value
195 */
196 bool contains(string accessor, T, Q)(ref T[] arr, Q val)
197 {
198     for(size_t i = 0; i < arr.length; i++)
199     {
200         if(__traits(getMember, arr[i], accessor) == val)
201             return true;
202     }
203     return false;
204 }
205 
206 /**
207 *   Compare two different structures accessing different members
208 */
209 bool contains(string accessorA, string accessorB, T, Q)(ref T[] arr, Q val)
210 {
211     for(size_t i = 0; i < arr.length; i++)
212     {
213         if(__traits(getMember, arr[i], accessorA) == __traits(getMember, val, accessorB))
214             return true;
215     }
216     return false;
217 }
218 
219 pragma(inline, true) bool isEmpty(T)(in T[] arr){return arr.length == 0;}
220 
221 
222 import std.traits:ForeachType;
223 ForeachType!(T)[] array(T)(T range)
224 {
225     typeof(return) ret;
226     foreach(r;range)
227         ret~= r;
228     return ret;
229 }
230 
231 string join(T)(T[] arr, string separator = "")
232 {
233     import hip.util.conv;
234     string ret;
235     if(arr.length == 0) return "";
236 
237     ret = toString(arr[0]);
238     for(int i = 1; i < arr.length; i++)
239     {
240         ret~= separator;
241         ret~= arr[i];
242     }
243     return ret;
244 }
245 
246 string join(T)(T[] arr, char separator)
247 {
248     char[1] temp = separator;
249     return join(arr, cast(string)temp);
250 }
251 
252 pragma(inline, true)
253 T uninitializedArray(T)(size_t size)
254 {
255     version(UseCustomRT)
256     {
257         return object._d_newarrayU!(typeof(T.init[0]))(size);
258     }
259 	else version(D_ProfileGC)
260     {
261         T ret;
262         ret.length = size;
263         return ret;
264     }
265     else
266     {
267         static import std.array;
268         return std.array.uninitializedArray!T(size);
269     }
270 }
271 
272 
273 void printArrayWithoutValues(T)(const T[] arr, T[] ignoreValues...)
274 {
275     import hip.console.log;
276     string str = "[";
277     MainLoop: foreach (val; arr)
278     {
279         foreach (arg; ignoreValues)
280             if(val == arg) //Ignore if value is in the loop list
281                 continue MainLoop;
282         str~= to!string(val) ~", ";
283     }
284     str~= "]";
285     const int index = lastIndexOf(str, ',');
286     if(index == -1)
287         logln("[]");
288     else
289         logln(str[0..index] ~ str[index+2..$]);
290 }
291 
292 unittest
293 {
294     assert(indexOf([2, 3, 4], 3) == 1);
295     assert(swapElementsFromArray([5, 10, 9], 10, 9));
296     assert(lastIndexOf([2, 3, 3, 4], 4) == 3);
297 }